﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;

namespace Lilium.Web.Mvc.AjaxControlToolkit
{
	public class ClientScriptResource
	{
		public ClientScriptResource(
			string name, 
			string resourceName, 
			string resourceTypeName,
			Assembly resourceAssembly)
		{
			if (string.IsNullOrEmpty(name))
				throw new ArgumentNullOrEmptyException("name");
			if (string.IsNullOrEmpty(resourceName))
				throw new ArgumentNullOrEmptyException("resourceName");
			if (string.IsNullOrEmpty(resourceTypeName))
				throw new ArgumentNullOrEmptyException("clientResourceClass");
			if (resourceAssembly == null)
				throw new ArgumentNullException("resourceAssembly");

			m_Name = name;
			m_ResourceAssembly = resourceAssembly;
			m_ResourceName =
				resourceName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)
				? resourceName.Substring(0, resourceName.Length - 10)
				: resourceName;
			m_ResourceTypeName = resourceTypeName;
		}

		public void ProcessRequest(ControllerContext context)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			var outputWriter = context.HttpContext.Response.Output;
			// Found a matching script resource; write it out
			outputWriter.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}={{", ResourceTypeName));

			// Load a ResourceManager/ResourceSet and walk through the list to output them all
			System.Resources.ResourceManager resourceManager = new System.Resources.ResourceManager(ResourceName, ResourceAssembly);
			using (System.Resources.ResourceSet resourceSet = resourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, true))
			{
				bool first = true;
				foreach (System.Collections.DictionaryEntry de in resourceSet)
				{
					if (!first)
					{
						// Need a comma between all entries
						outputWriter.Write(",");
					}
					// Output the entry
					string name = (string)de.Key;
					string value = resourceManager.GetString(name);
					outputWriter.Write(string.Format(CultureInfo.InvariantCulture, "\"{0}\":\"{1}\"", QuoteString(name), QuoteString(value)));
					first = false;
				}
			}
			outputWriter.WriteLine("};");
		}

		#region public string Name

		private readonly string m_Name;

		public string Name { [DebuggerNonUserCodeAttribute] get { return m_Name; } }

		#endregion

		/// <summary>
		/// Callable implementation of System.Web.Script.Serialization.JavaScriptString.AppendCharAsUnicode
		/// </summary>
		/// <param name="builder">string builder</param>
		/// <param name="c">character to append</param>
		[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Callable implementation of System.Web.Script.Serialization.JavaScriptString.AppendCharAsUnicode")]
		[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c", Justification = "Callable implementation of System.Web.Script.Serialization.JavaScriptString.AppendCharAsUnicode")]
		private static void AppendCharAsUnicode(StringBuilder builder, char c)
		{
			builder.Append(@"\u");
			builder.AppendFormat(CultureInfo.InvariantCulture, "{0:x4}", new object[] { (int)c });
		}

		/// <summary>
		/// Callable implementation of System.Web.Script.Serialization.JavaScriptString.QuoteString
		/// </summary>
		/// <param name="value">value to quote</param>
		/// <returns>quoted string</returns>
		[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Callable implementation of System.Web.Script.Serialization.JavaScriptString.QuoteString")]
		private static string QuoteString(string value)
		{
			StringBuilder builder = null;
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			int startIndex = 0;
			int count = 0;
			for (int i = 0; i < value.Length; i++)
			{
				char c = value[i];
				if ((((c == '\r') || (c == '\t')) || ((c == '"') || (c == '\''))) || ((((c == '<') || (c == '>')) || ((c == '\\') || (c == '\n'))) || (((c == '\b') || (c == '\f')) || (c < ' '))))
				{
					if (builder == null)
					{
						builder = new StringBuilder(value.Length + 5);
					}
					if (count > 0)
					{
						builder.Append(value, startIndex, count);
					}
					startIndex = i + 1;
					count = 0;
				}
				switch (c)
				{
					case '<':
					case '>':
					case '\'':
						{
							AppendCharAsUnicode(builder, c);
							continue;
						}
					case '\\':
						{
							builder.Append(@"\\");
							continue;
						}
					case '\b':
						{
							builder.Append(@"\b");
							continue;
						}
					case '\t':
						{
							builder.Append(@"\t");
							continue;
						}
					case '\n':
						{
							builder.Append(@"\n");
							continue;
						}
					case '\f':
						{
							builder.Append(@"\f");
							continue;
						}
					case '\r':
						{
							builder.Append(@"\r");
							continue;
						}
					case '"':
						{
							builder.Append("\\\"");
							continue;
						}
				}
				if (c < ' ')
				{
					AppendCharAsUnicode(builder, c);
				}
				else
				{
					count++;
				}
			}
			if (builder == null)
			{
				return value;
			}
			if (count > 0)
			{
				builder.Append(value, startIndex, count);
			}
			return builder.ToString();
		}

		#region private Assembly ResourceAssembly

		private readonly Assembly m_ResourceAssembly;

		private Assembly ResourceAssembly { [DebuggerNonUserCodeAttribute] get { return m_ResourceAssembly; } }

		#endregion

		#region private string ResourceName

		private readonly string m_ResourceName;

		private string ResourceName { [DebuggerNonUserCodeAttribute] get { return m_ResourceName; } }

		#endregion

		#region private ResourceTypeName ResourceTypeName

		private readonly string m_ResourceTypeName;

		private string ResourceTypeName { [DebuggerNonUserCodeAttribute] get { return m_ResourceTypeName; } }

		#endregion
	}
}
